package com.tarot.aspect.logger.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * 请求日志切面
 *
 * @author zhaohejia
 * @version 1.0.0
 * @date 2020/11/6
 */
//@Component
//@Aspect
public class SimpleRequestLogAspect {

    /**
     * 日志
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(SimpleRequestLogAspect.class);

    /**
     * 切点：Controller包下的所有类下的方法
     */
    @Pointcut("execution(* com.tarot.aspect.logger.controller..*(..))")
    public void requestServer() {
    }

    /**
     * 前置通知：在进入Controller方法前(在切点之前执行代码)，打印出调用方IP、请求URL、HTTP请求类型、调用的方法名
     *
     * @param joinPoint 切点
     */
    @Before("requestServer()")
    public void doBefore(JoinPoint joinPoint) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert attributes != null;
        HttpServletRequest request = attributes.getRequest();

        LOGGER.info("===============================Start========================");
        LOGGER.info("IP                 : {}", request.getRemoteAddr());
        LOGGER.info("URL                : {}", request.getRequestURL().toString());
        LOGGER.info("HTTP Method        : {}", request.getMethod());
        LOGGER.info("Class Method       : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
    }

    /**
     * 环绕通知：在进入Controller方法后开始执行逻辑代码前，打印了入参、结果以及耗时
     *
     * @param point 切点
     */
    @Around("requestServer()")
    public Object doAround(ProceedingJoinPoint point) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = point.proceed();

        LOGGER.info("Request Params       : {}", getRequestParams(point));
        LOGGER.info("Result               : {}", result);
        LOGGER.info("Time Cost            : {} ms", System.currentTimeMillis() - start);
        return result;
    }

    /**
     * 后置通知：
     */
    @After("requestServer()")
    public void doAfter() {
        LOGGER.info("===============================End========================");
    }


    /**
     * 获取请求参数集合
     *
     * @param point 切点
     * @return 请求参数集合
     */
    private Map<String, Object> getRequestParams(ProceedingJoinPoint point) {

        //参数名
        String[] paramNames = ((MethodSignature) point.getSignature()).getParameterNames();
        //参数值
        Object[] paramValues = point.getArgs();

        Map<String, Object> requestParams = new HashMap<>(paramNames.length);
        //以key=参数名,value=参数值组装入参对象
        for (int i = 0; i < paramNames.length; i++) {
            Object paramValue = paramValues[i];
            //如果是文件对象
            if (paramValue instanceof MultipartFile) {
                MultipartFile file = (MultipartFile) paramValue;
                //文件展示的值为文件名
                paramValue = file.getOriginalFilename();
            }
            requestParams.put(paramNames[i], paramValue);
        }

        return requestParams;
    }
}
